SOLARPOSITION
Overview
The SOLARPOSITION function calculates the position of the sun in the sky—specifically the azimuth, elevation, and apparent zenith angles—for a given time and geographic location. These values are essential for photovoltaic system design, solar tracking, shading analysis, and solar resource assessment.
This implementation uses the pvlib Python library, which provides a comprehensive set of tools for simulating photovoltaic energy systems. The underlying solar position calculations are based on the NREL Solar Position Algorithm (SPA), developed by the National Renewable Energy Laboratory. The SPA algorithm calculates solar zenith and azimuth angles with uncertainties of ±0.0003 degrees for dates between the years -2000 and 6000. For more details, see the pvlib solar position documentation and the NREL SPA website.
The algorithm is based on the astronomical algorithms published in:
Reda, I.; Andreas, A. (2004). Solar Position Algorithm for Solar Radiation Applications. Solar Energy, Vol. 76(5), pp. 577-589. NREL Technical Report
The function returns three key angles:
- Azimuth: The compass direction of the sun measured clockwise from true north (0° = North, 90° = East, 180° = South, 270° = West)
- Elevation: The angle of the sun above the horizon (0° at horizon, 90° at zenith)
- Apparent Zenith: The angle from the vertical to the sun’s position, corrected for atmospheric refraction (90° - elevation, adjusted)
The function supports multiple calculation methods including nrel_numpy (default, recommended), nrel_numba (compiled for speed), pyephem, ephemeris, and nrel_c. Atmospheric refraction corrections use the provided temperature and pressure parameters to improve accuracy for low solar elevation angles.
This example function is provided as-is without any representation of accuracy.
Excel Usage
=SOLARPOSITION(time, latitude, longitude, altitude, pressure, method, temperature)
time(str, required): The time valuelatitude(float, required): The latitude valuelongitude(float, required): The longitude valuealtitude(float, optional, default: 0): The altitude valuepressure(float, optional, default: 101325): The pressure valuemethod(str, optional, default: “nrel_numpy”): The method valuetemperature(float, optional, default: 12): The temperature value
Returns (list[list]): 2D list [[azimuth, elevation, apparent_zenith]], or error string.
Examples
Example 1: Demo case 1
Inputs:
| time | latitude | longitude | altitude | pressure | method | temperature |
|---|---|---|---|---|---|---|
| 2024-06-20T12:00:00Z | 35 | -120 | 0 | 101325 | nrel_numpy | 12 |
Excel formula:
=SOLARPOSITION("2024-06-20T12:00:00Z", 35, -120, 0, 101325, "nrel_numpy", 12)
Expected output:
| Result | ||
|---|---|---|
| 53.2 | -8.8 | 98.8 |
Example 2: Demo case 2
Inputs:
| time | latitude | longitude | altitude | pressure | method | temperature |
|---|---|---|---|---|---|---|
| 2024-06-20T06:00:00Z | 35 | -120 | 0 | 101325 | nrel_numpy | 12 |
Excel formula:
=SOLARPOSITION("2024-06-20T06:00:00Z", 35, -120, 0, 101325, "nrel_numpy", 12)
Expected output:
| Result | ||
|---|---|---|
| 329.2 | -24.8 | 114.8 |
Example 3: Demo case 3
Inputs:
| time | latitude | longitude | altitude | pressure | method | temperature |
|---|---|---|---|---|---|---|
| 2024-12-21T12:00:00Z | 35 | -120 | 100 | 90000 | nrel_numpy | 12 |
Excel formula:
=SOLARPOSITION("2024-12-21T12:00:00Z", 35, -120, 100, 90000, "nrel_numpy", 12)
Expected output:
| Result | ||
|---|---|---|
| 94.7 | -36.8 | 126.8 |
Example 4: Demo case 4
Inputs:
| time | latitude | longitude | altitude | pressure | method | temperature |
|---|---|---|---|---|---|---|
| 2024-06-20T12:00:00Z | 35 | -120 | 0 | 101325 | nrel_numpy | 25 |
Excel formula:
=SOLARPOSITION("2024-06-20T12:00:00Z", 35, -120, 0, 101325, "nrel_numpy", 25)
Expected output:
| Result | ||
|---|---|---|
| 53.2 | -8.8 | 98.8 |
Python Code
import micropip
await micropip.install(["pvlib"])
import pandas as pd
from pvlib.solarposition import get_solarposition as pvlib_get_solarposition
def solarposition(time, latitude, longitude, altitude=0, pressure=101325, method='nrel_numpy', temperature=12):
"""
Calculate solar azimuth, elevation, and apparent zenith for given times and location.
See: https://pvlib-python.readthedocs.io/en/stable/reference/generated/pvlib.solarposition.get_solarposition.html
This example function is provided as-is without any representation of accuracy.
Args:
time (str): The time value
latitude (float): The latitude value
longitude (float): The longitude value
altitude (float, optional): The altitude value Default is 0.
pressure (float, optional): The pressure value Default is 101325.
method (str, optional): The method value Valid options: NREL NumPy, NREL Numba, PyEphem, Ephemeris, NREL C. Default is 'nrel_numpy'.
temperature (float, optional): The temperature value Default is 12.
Returns:
list[list]: 2D list [[azimuth, elevation, apparent_zenith]], or error string.
"""
# Helper function to normalize inputs
def to2d(value):
if not isinstance(value, list):
return [[value]]
return value
# Normalize time input
time_2d = to2d(time)
# Validate time input format
if not (isinstance(time_2d, list) and all(isinstance(row, list) and len(row) == 1 and isinstance(row[0], str) for row in time_2d)):
return [["Error: Time must be a single string or a 2D list of single string rows."]]
try:
times = [row[0] for row in time_2d]
dt_index = pd.DatetimeIndex(times)
except Exception:
return [["Error: Time values must be ISO8601 datetime strings."]]
try:
lat = float(latitude)
lon = float(longitude)
alt = float(altitude)
pres = float(pressure)
temp = float(temperature)
meth = str(method)
except Exception:
return [["Error: Latitude, longitude, altitude, pressure, temperature must be numbers."]]
try:
df = pvlib_get_solarposition(
time=dt_index,
latitude=lat,
longitude=lon,
altitude=alt,
pressure=pres,
method=meth,
temperature=temp
)
# Extract azimuth, elevation, apparent_zenith
result = []
for i in range(len(dt_index)):
az = float(df.iloc[i]["azimuth"])
el = float(df.iloc[i]["elevation"])
zen = float(df.iloc[i]["apparent_zenith"])
result.append([az, el, zen]) # Don't round as per guidelines
return result
except Exception as e:
return [[f"Error: pvlib - {e}"]]